package com.hcss.common.util;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

/**
 * @function : 反射工具类
 */
@SuppressWarnings("rawtypes")
public class ReflectUtil {
	
	/**
	 * 将集合的对象的属性进行展现，展现的格式：[{attr:value,attr1:value1},{attr:value,attr1:value1}]
	 * @param list
	 * @return
	 */
	public static String showListAttrAndValue(List<?> list){
		if(EmptyUtil.isEmpty(list)){
			return null;
		}
		StringBuffer result = new StringBuffer();
		int length = list.size();
		for(int i = 0 ; i < length; i++){
			Object object = list.get(i);
			result.append("{" + showAttrAndValue(object) + "},");
		}
		
		return result.length() > 1 ? result.substring(0, result.length()) : null;
	}
	
	/**
	 * 展现属性的值，展现的方式是{attr:value,attr1:value1}
	 * @param object
	 * @return
	 */
	public static String showAttrAndValue(Object object){
		if(EmptyUtil.isHaveEmpty(object)){
			return null;
		}
		StringBuffer result = new StringBuffer();
		
		Class<?> claz = object.getClass();
		Field[] fields = claz.getDeclaredFields();
		
		for(Field field : fields){
			String fieldName = field.getName();
			field.setAccessible(true);
			try {
				Object value = field.get(object);
				
				result.append(fieldName + ":" +value+ ",");
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		
		return result.length() > 1 ? result.substring(0, result.length()) : null;
	}
	
	/**
	 * 判断这个类是否存在这个属性
	 * @param claz
	 * @param attrName
	 * @return
	 */
	public static Boolean isHadAttr(Class<?> claz, String attrName){
		if(EmptyUtil.isHaveEmpty(claz, attrName)){
			return false;
		}
		
		try {
			Field field = claz.getDeclaredField(attrName);//公开属性
			if(field == null){
				field = claz.getField(attrName);//自身属性
			}
			
			return field != null;
		} catch (Exception e) {
			return false;
		}
	}
	
	/**
	 * 指定的方法名和对象进行执行获取数据
	 * @param object
	 * @param methodName
	 * @param parameterClass
	 * @param parameterValue
	 * @return
	 * @throws Exception
	 */
	public static Object executeMethodValue(Object object, String methodName, 
			Class<?> parameterClass, Object parameterValue) throws Exception{
		return object.getClass().getMethod(methodName, parameterClass).invoke(object, parameterValue);
	}
	
	/**
	 * 没有参数的方法执行
	 * @param object
	 * @param methodName
	 * @return
	 * @throws Exception
	 */
	public static Object executeMethodValue(Object object, String methodName) throws Exception{
		return object.getClass().getMethod(methodName).invoke(object);
	}
	
	/**
	 * 根据属性名称获取对象对应属性的值
	 * @param object
	 * @param attrName
	 * @return
	 * @throws Exception
	 */
	public static Object getValue(Object object, String attrName) throws Exception{
		//先判断是否存在标准规则的方法名，如果不存在，则通过不敏感大小写来匹配方法名
		Object result = getValueByMethod(object, attrName);
		return result == null ? getValueByMethods(object, attrName) : result;
	}
	
	/**
	 * 通过所有的方法进行非大小写敏感匹配get方法名进行获取值
	 * @param object
	 * @param attrName
	 * @return
	 */
	public static Object getValueByMethods(Object object, String attrName){
		try {
			String methodName = "get" + StringUtil.upperFirstChar(attrName);
			Method[] methods = object.getClass().getMethods();
			if(EmptyUtil.isEmpty(methods)){
				return null;
			}
			for(Method method : methods){
				if(method.getName().equalsIgnoreCase(methodName)){
					return method.invoke(object);
				}
			}
			
		} catch (Exception e) {
			System.out.println(ThreadUtil.getCurrentMethodName() + ",error");
		}
		return null;
	}
	
	/**
	 * 通过规则的拼接方法名获取属性值
	 * @param object
	 * @param attrName
	 * @return
	 */
	public static Object getValueByMethod(Object object, String attrName){
		try {
			Method getMethod = object.getClass().getMethod("get" + StringUtil.upperFirstChar(attrName));
			return getMethod == null ? null : getMethod.invoke(object);
		} catch (Exception e) {
			System.out.println(ThreadUtil.getCurrentMethodName() + ",error");
            return null;
		}
	}
	
	/**
	 * 判断是否是基础类
	 * @param claz
	 * @return
	 */
	public static Boolean isBasicClass(Class<?> claz){
		if(EmptyUtil.isHaveEmpty(claz)){
			return false;
		}
		try {
			Field typeField = claz.getDeclaredField("TYPE");
			return typeField != null;
		} catch (NoSuchFieldException | SecurityException e) {
			return false;
		}
	}
	
	/**
	 * TODO 设置value不同方式
	 * 向指定域名输入值
	 * 注意基本类型的装包和拆包问题
	 * @param object
	 * @param attrName
	 * @param attrValue
	 * @throws Exception
	 */
	public static void setValue(Object object, String attrName, Object attrValue)
			throws Exception{
		if(EmptyUtil.isHaveEmpty(attrValue)){
			return;
		}
		try {
			setValueByMethod(object, attrName, attrValue);
			return;
		} catch (Exception e) {
			System.out.println(ThreadUtil.getCurrentMethodName() + ",error");
		}
		try {
			setValueByMethods(object, attrName, attrValue);
		} catch (Exception e) {
			System.out.println(ThreadUtil.getCurrentMethodName() + ",error");
		}
	}
	
	/**
	 * 通过所有的方法进行非大小写敏感匹配
	 * @param object
	 * @param attrName
	 * @param attrValue
	 * @throws Exception
	 */
	public static void setValueByMethods(Object object, String attrName, Object attrValue) 
			throws Exception{
		String methodName = "set" + StringUtil.upperFirstChar(attrName);
		Method[] methods = object.getClass().getMethods();
		if(EmptyUtil.isEmpty(methods)){
			return;
		}
		for(Method method : methods){
			if(method.getName().equalsIgnoreCase(methodName)){
				method.invoke(object, attrValue);
				return;
			}
		}
	}
	
	/**
	 * 通过setMethod方法对属性赋值
	 * @param object
	 * @param attrName
	 * @param attrValue
	 * @throws Exception
	 */
	public static void setValueByMethod(Object object, String attrName, Object attrValue)
			throws Exception{
		
		Class<?> clz = object.getClass();
		String methodName = "set" + StringUtil.upperFirstChar(attrName);
		
		Method setMethod = null;
		try {
			setMethod = clz.getMethod(methodName, attrValue.getClass());
		} catch (Exception e) {//为了识别基本类型，并用基本类型进行进行转换解析
			Field typeField = attrValue.getClass().getDeclaredField("TYPE");
			if(typeField == null){
				throw e;
			}
			setMethod = clz.getMethod(methodName, (Class<?>) typeField.get(attrValue));
		}
		setMethod.invoke(object, attrValue);
	}
	
	/**
	 * 以字符串形式向指定属性set值
	 * @param object
	 * @param attrName
	 * @param attrValue
	 * @throws Exception
	 */
	public static void setValueByString(Object object, String attrName, String attrValue) throws Exception{
		if(EmptyUtil.isHaveEmpty(object, attrName, attrValue)){
			return;
		}
		Class<?> clz = object.getClass();
		// update by maxiaoming 2018-06-01 修改获取Field的方式，以获取父类的字段
		Field field = getField(clz, attrName);
		Class<?> fieldClaz = field.getType();
		
		Object value = fieldClaz.getConstructor(String.class).newInstance(attrValue);
		setValue(object, attrName, value);
	}
	
	/**
	 * 直接对对象的某个方法，进行invoke操作
	 * @param object
	 * @param attrName
	 * @param attrValues
	 * @return
	 * @throws Exception
	 * @throws NoSuchFieldException
	 */
	public static Object invoke(Object object, String methodName, Object... attrValues) 
			throws Exception, NoSuchFieldException{
		Class<?> clz = object.getClass();
		Method method = null;
		
		if(EmptyUtil.isEmpty(attrValues)){
			method = clz.getMethod(methodName);
		}else{
			Class<?>[] parameterTypes = new Class<?>[attrValues.length];
			for(int i = 0; i < parameterTypes.length; i++){
				parameterTypes[i] = attrValues.getClass();
			}
			method = clz.getMethod(methodName, parameterTypes);
		}
		if(method == null){
			return null;
		}
		return method.invoke(object, attrValues);
	}
	
	/**
	 * 根据类，方法名以及参数获取方法
	 * @param claz
	 * @param methodName
	 * @param args
	 * @return
	 */
	public static Method getMethod(Class<?> claz, String methodName, Object[] args){
		if(EmptyUtil.isHaveEmpty(methodName, claz)){
			return null;
		}
		
		Method method = null;
		Class[] argClazs = null;
		
		if(!EmptyUtil.isEmpty(args)){
			argClazs = new Class[args.length];
			for(int i = 0 ; i < args.length; i++){
				argClazs[i] = args[i].getClass();
			}
		}
		
		try {
			method = claz.getMethod(methodName, argClazs);
		}catch(Exception e){
			e.printStackTrace();
		}
		return method;
	}
	
	/**
	 * 获取自己和父类的所有的属性：私有，默认，保护，公有的属性都有
	 * @param claz
	 * @return
	 */
	public static List<Field> getAllFields(Class<?> claz){
		List<Field> fields = new ArrayList<Field>();
		if(claz == null){
			return fields;
		}
		if(claz.equals(Object.class)){
			return fields;
		}
		
		Field[] selfFields = claz.getDeclaredFields();
		Collections.addAll(fields, selfFields);
		fields.addAll(getAllFields(claz.getSuperclass()));
		
		return fields;
	}
	
	/**
	 * 在claz类型下获取指定域名的域
	 * @param claz
	 * @param fieldName
	 * @return
	 */
	public static Field getField(Class<?> claz, String fieldName){
		if(claz == null || claz.equals(Object.class)){
			return null;
		}
		try {
			return claz.getDeclaredField(fieldName);
		} catch (NoSuchFieldException | SecurityException e) {
			return getField(claz.getSuperclass(), fieldName);
		}
	}
	
	/**
	 * 解析Map集合为对象集合
	 * @param list
	 * @param claz
	 * @return
	 * @throws Exception
	 */
	public static<T> List<T> parse(List<Map<String, String>> list, Class<T> claz) throws Exception{
		List<T> result = new ArrayList<T>();
		
		if(EmptyUtil.isEmpty(list)){
			return result;
		}
		
		for(Map<String, String> one : list){
			result.add(parseMap(one, claz));
		}
		return result;
	}
	
	/**
	 * 将Map集合（map里面只有两个key）转换成单个map，类似行列转换
	 * @param list
	 * @param keyName
	 * @param valueName
	 * @return
	 */
	public static Map<String, String> parse(List<Map<String, String>> list, String keyName, String valueName){
		Map<String, String> result = new HashMap<String, String>();
		if(EmptyUtil.isEmpty(list)){
			return result;
		}
		for(Map<String, String> one : list){
			result.put(one.get(keyName), one.get(valueName));
		}
		return result;
	}
	
	/**
	 * 按照claz格式解析map数据成对象
	 * @param one
	 * @param claz
	 * @return
	 * @throws Exception
	 */
	public static<T> T parseMap(Map<String, String> one, Class<T> claz) throws Exception{
		T result = claz.newInstance();
		
		Object value = null;
		Set<Entry<String, String>> kvs = one.entrySet();
		for(Entry<String, String> kv : kvs){
			String key = kv.getKey();
			String valueTmp = kv.getValue();
			
			Field field = claz.getField(key);
			Class<?> fieldClaz = field.getType();
			
			if(Date.class.equals(fieldClaz)){
				value = DateUtil.stringToDate(valueTmp, DateUtil.FORMAT_yMdHmsS);
			}else{
				value = fieldClaz.getConstructor(String.class).newInstance(valueTmp);
			}
			
			ReflectUtil.setValue(result, key, value);
		}
		return result;
	}
	
	/**
	 * 将one根据key和属性对应设置到实例对象上
	 * @param one
	 * @param result
	 * @throws Exception
	 */
	public static<T> void parseMap(Map<String, String> one, T result) throws Exception{
		Class<?> claz = result.getClass();
		
		Object value = null;
		Set<Entry<String, String>> kvs = one.entrySet();
		for(Entry<String, String> kv : kvs){
			String key = kv.getKey();
			String valueTmp = kv.getValue();
			
			Field field = claz.getField(key);
			Class<?> fieldClaz = field.getType();
			
			if(Date.class.equals(fieldClaz)){
				value = DateUtil.stringToDate(valueTmp, DateUtil.FORMAT_yMdHmsS);
			}else{
				value = fieldClaz.getConstructor(String.class).newInstance(valueTmp);
			}
			
			ReflectUtil.setValue(result, key, value);
		}
	}
	
	    public static Object getFieldValue(Object object, String fieldName){  
	        
	        //根据 对象和属性名通过反射 调用上面的方法获取 Field对象  
	        Field field = getDeclaredField(object, fieldName) ;  
	          
	        //抑制Java对其的检查  
	        field.setAccessible(true) ;  
	          
	        try {  
	            //获取 object 中 field 所代表的属性值  
	            return field.get(object) ;  
	              
	        } catch(Exception e) {  
	            e.printStackTrace() ;  
	        }  
	          
	        return null;  
	    } 
	    
	    public static Field getDeclaredField(Object object, String fieldName){  
	        Field field = null ;  
	          
	        Class<?> clazz = object.getClass() ;  
	          
	        for(; clazz != Object.class ; clazz = clazz.getSuperclass()) {  
	            try {  
	                field = clazz.getDeclaredField(fieldName) ;  
	                return field ;  
	            } catch (Exception e) {  
	                //这里甚么都不要做！并且这里的异常必须这样写，不能抛出去。  
	                //如果这里的异常打印或者往外抛，则就不会执行clazz = clazz.getSuperclass(),最后就不会进入到父类中了  
	                  
	            }   
	        }  
	      
	        return null;  
	    } 
}
